﻿using Avalonia.Controls;
using LightCAD.Core;
using LightCAD.Core.Elements;
using LightCAD.Drawing;
using LightCAD.Runtime;
using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static LightCAD.Core.Elements.LcPolyLine;

namespace QdArch
{
    public partial class SlabAction : ElementAction
    {
        public enum EmbedType
        {
            Hole,
            UpSlab,
            DownSlab
        };

        private static readonly LcCreateMethod[] CreateMethods;

        private List<Vector2d> outPoints;
        private List<List<Vector2d>> holes;
        private Dictionary<EmbedType, List<QdUpDownSlab>> upDownSlabs;
        private Vector2d? firstPoint;
        private Vector2d? secondPoint;
        private PointInputer pointInputer;
        private CmdTextInputer cmdTextInputer;
        private List<LcLine> tempLines;

        public SlabAction() { }

        public SlabAction(DocumentRuntime docRt) : base(docRt)
        {
            docRt.CommandCtrl.WriteInfo("命令：Slab");
        }

        static SlabAction()
        {
            CreateMethods = new LcCreateMethod[1];
            CreateMethods[0] = new LcCreateMethod()
            {
                Name = "CreateSlab",
                Description = "创建楼板",
                Steps = new LcCreateStep[]
                {
                    new LcCreateStep { Name = "Step0", Options = "指定轮廓第一个点:" },
                    new LcCreateStep { Name = "Step1", Options = "指定轮廓下一点或 [结束(E)]" },
                    new LcCreateStep { Name = "Step2", Options = "选择创建 [洞口(H)/升板(W)/降板(S)/放弃(U)]" },
                    new LcCreateStep { Name = "Step3", Options = "指定洞口(升板/降板)第一个点或 [放弃(U)]" },
                    new LcCreateStep { Name = "Step4", Options = "指定洞口(升板/降板)下一个点或 [结束(E)]" },
                    new LcCreateStep { Name = "Step5", Options = "设置降板(升板)底高度" },
                    new LcCreateStep { Name = "Step6", Options = "指定板厚<200>或 [放弃(U)]" }
                }
            };
        }

        internal static void Initilize()
        {
            ArchActions.SlabAction = new SlabAction();
            LcDocument.ElementActions.Add(ArchElementType.Slab, ArchActions.SlabAction);
        }

        public async void ExecCreate(string[] args = null)
        {
            this.tempLines = new List<LcLine>();
            this.outPoints = new List<Vector2d>();
            this.holes = new List<List<Vector2d>>();
            this.upDownSlabs = new Dictionary<EmbedType, List<QdUpDownSlab>>()
            {
                { EmbedType.UpSlab, new List<QdUpDownSlab>() },
                { EmbedType.DownSlab, new List<QdUpDownSlab>() },
            };
            this.StartCreating();
            this.pointInputer = new PointInputer(this.docRt);
            this.cmdTextInputer = new CmdTextInputer(this.docRt);
            var curMethod = CreateMethods[0];

            var doc = this.docRt.Document;
        Step0: //选择外轮廓第一个点
            var step0 = curMethod.Steps[0];
            var result0 = await pointInputer.Execute(step0.Options);
            if (pointInputer.isCancelled)
            {
                this.Cancel();
                goto End;
            }

            if (result0 == null)
            {
                this.Cancel();
                goto End;
            }

            if (result0.Value == null)
            {
                if (result0.Option != null)
                {
                    goto Step0;
                    //TODO:AutoCAD画线输入一个数字，是怎么确定点的？
                }
                else
                {
                    goto Step0;
                }
            }

            this.firstPoint = (Vector2d)result0.Value;
            this.outPoints.Add(this.firstPoint.Value);

        Step1: //绘制外轮廓
            var step1 = curMethod.Steps[1];
            var result1 = await pointInputer.Execute(step1.Options);
            if (pointInputer.isCancelled)
            {
                this.Cancel();
                goto End;
            }

            if (result1.Value == null)
            {
                if (result1.Option == "C")
                {
                    goto End;
                }
                else if (result1.Option != null)
                {
                    docRt.CommandCtrl.WriteInfo(SR.PointError);
                    goto Step2;
                }
                else
                {
                    goto Step2;
                }
            }

            this.secondPoint = (Vector2d)result1.Value;
            this.outPoints.Add(secondPoint.Value);
            AppendTempLine(this.firstPoint.Value, this.secondPoint.Value);
            if (this.outPoints.Count > 2 && this.secondPoint.Value.Equals(this.outPoints[0])) //首位相连直接进入下一步
            {
                firstPoint = null;
                goto Step2;
            }
            firstPoint = secondPoint;
            goto Step1;

        Step2: //选择绘制内部洞口/升板/降板, 或跳过
            var curEmbedType = EmbedType.Hole;
            var step2 = curMethod.Steps[2];
            var result2 = await cmdTextInputer.Execute(step2.Options);
            if (cmdTextInputer.isCancelled)
            {
                goto Step6;
            }
            if (result2 == null)
            {
                goto Step6;
            }
            if (string.IsNullOrEmpty(result2.Value))
            {
                goto Step2;
            }
            switch (result2.Value.ToUpper())
            {
                case "H":
                    curEmbedType = EmbedType.Hole;
                    break;
                case "W":
                    curEmbedType = EmbedType.UpSlab;
                    break;
                case "S":
                    curEmbedType = EmbedType.DownSlab;
                    break;
                default:
                    break;
            }

        Step3: //选择洞口/升板/降板第一个点
            var step3 = curMethod.Steps[3];
            var curInnerLine = new List<Vector2d>();
            var result3 = await pointInputer.Execute(step3.Options);
            if (pointInputer.isCancelled)
            {
                goto Step2;
            }
            if (result3 == null)
            {
                goto Step2;
            }

            if (result3.Value == null)
            {
                if (result3.Option != null)
                {
                    goto Step3;
                    //TODO:AutoCAD画线输入一个数字，是怎么确定点的？
                }
                else
                {
                    goto Step3;
                }
            }

            this.firstPoint = (Vector2d)result3.Value;
            curInnerLine.Add(this.firstPoint.Value);

        Step4: //绘制洞口/升板/降板外轮廓
            var step4 = curMethod.Steps[4];
            var result4 = await pointInputer.Execute(step4.Options);
            if (pointInputer.isCancelled)
            {
                goto Step2;
            }
            if (result4 == null)
            {
                goto Step2;
            }

            if (result4.Value == null)
            {
                if (result4.Option != null)
                {
                    goto Step4;
                    //TODO:AutoCAD画线输入一个数字，是怎么确定点的？
                }
                else
                {
                    goto Step4;
                }
            }

            this.secondPoint = (Vector2d)result4.Value;
            curInnerLine.Add(this.secondPoint.Value);
            AppendTempLine(this.firstPoint.Value, this.secondPoint.Value);
            if (curInnerLine.Count > 3 && this.secondPoint.Value.Equals(curInnerLine[0])) //首位相连直接进入下一步
            {
                if (curEmbedType == EmbedType.Hole)
                {
                    this.holes.Add(curInnerLine);
                }
                else if(curEmbedType == EmbedType.UpSlab || curEmbedType == EmbedType.DownSlab)
                {
                    goto Step5;
                }
                this.firstPoint = null;
                goto Step2;
            }

            firstPoint = secondPoint;
            goto Step3;

        Step5: //如果是升板/降板, 需要设置底高度
            var step5 = curMethod.Steps[5];
            var result5 = await cmdTextInputer.Execute(step5.Options);
            if (cmdTextInputer.isCancelled)
            {
                goto Step2;
            }
            if (result5 == null || string.IsNullOrEmpty(result5.Value))
            {
                goto Step2;
            }

            if (!double.TryParse(result5.Value, out double bottom))
            {
                goto Step5;
            }


            var embedSlab = new QdUpDownSlab(curInnerLine)
            {
                Bottom = bottom
            };
            this.upDownSlabs[curEmbedType].Add(embedSlab);
            goto Step2;

        Step6: //设置楼板厚度

            var step6 = curMethod.Steps[6];
            var result6 = await cmdTextInputer.Execute(step6.Options);
            if (cmdTextInputer.isCancelled)
            {
                goto Step2;
            }
            if (result6 == null || string.IsNullOrEmpty(result6.Value))
            {
                goto Step6;
            }
            double thickness = 100; //默认值
            if (!double.TryParse(result6.Value, out  thickness))
            {
                goto Step6;
            }

            this.ClearTempLine();
            CreateSlab(thickness);

        End:
            if (this.outPoints.Count < 4) //小于三个点无法创建轮廓，轮廓需要手动闭合，所以将最后一个点设置为开始点
            {
                //ERROR: 无法创建
            }
            this.pointInputer = null;
            this.EndCreating();
        }

        public override void Cancel()
        {
            base.Cancel();
            this.ClearTempLine();
            this.outPoints.Clear();
            this.holes.Clear();
            this.upDownSlabs.Clear();
            this.vportRt.SetCreateDrawer(null);
        }

        public void AppendTempLine(Vector2d sp, Vector2d ep)
        {
            var line = this.docRt.Document.CreateObject<LcLine>();
            line.Start = sp;
            line.End = ep;
            this.tempLines.Add(line);

            this.vportRt.ActiveElementSet.InsertElement(line);
        }

        public void ClearTempLine()
        {
            foreach (var line in this.tempLines)
            {
                this.vportRt.ActiveElementSet.RemoveElement(line);
            }
        }

        public void CreateSlab(double thickness = 100)
        {
            var doc = this.docRt.Document;
            var slab = doc.CreateObject<QdSlab>();
            slab.OutLine = this.outPoints ?? new List<Vector2d>();
            foreach (var hole in this.holes)
            {
                slab.Holes.Add(hole);
            }
            foreach (var kvp in this.upDownSlabs)
            {
                if (kvp.Key == EmbedType.UpSlab)
                {
                    slab.UpSlabs = kvp.Value;
                }
                else if(kvp.Key == EmbedType.DownSlab)
                {
                    slab.DownSlabs = kvp.Value;
                }
            }
            slab.Thickness = thickness;
            doc.ModelSpace.InsertElement(slab);
            this.docRt.Action.ClearSelects();
        }

        public override void DrawAuxLines(SKCanvas canvas)
        {
            if (firstPoint != null)
            {
                var mp = this.vportRt.PointerMovedPosition.ToVector2d();
                var wcs_mp = this.vportRt.ConvertScrToWcs(mp);

                var sk_pre = this.vportRt.ConvertWcsToScr(firstPoint.Value).ToSKPoint();
                var sk_p = this.vportRt.ConvertWcsToScr(wcs_mp).ToSKPoint();
                //辅助元素的颜色 
                canvas.DrawLine(sk_pre, sk_p, Constants.auxElementPen);
            }
        }

        public override void Draw(SKCanvas canvas, LcElement element, Matrix3d matrix)
        {
            var slab = element as QdSlab;
            for (int i = 0; i < slab.OutLine.Count - 1; i++)
            {
                var sp = slab.OutLine[i];
                var ep = slab.OutLine[i + 1];
                var msp = matrix.MultiplyPoint(sp);
                var mep = matrix.MultiplyPoint(ep);

                var ssp = this.vportRt.ConvertWcsToScr(msp).ToSKPoint();
                var sep = this.vportRt.ConvertWcsToScr(mep).ToSKPoint();
                var pen = this.GetDrawPen(element);
                
                if (pen == Constants.defaultPen)
                {
                    using (var elePen = new SKPaint { Color = new SKColor(element.GetColorValue()), IsStroke = true })
                    {

                        canvas.DrawLine(ssp, sep, elePen);
                    }
                }
                else
                {
                    canvas.DrawLine(ssp, sep, pen);
                }
            }
            foreach (var innerPoints in slab.Holes)
            {
                for (int i = 0; i < innerPoints.Count - 1; i++)
                {
                    var sp = innerPoints[i];
                    var ep = innerPoints[i + 1];
                    var msp = matrix.MultiplyPoint(sp);
                    var mep = matrix.MultiplyPoint(ep);

                    var ssp = this.vportRt.ConvertWcsToScr(msp).ToSKPoint();
                    var sep = this.vportRt.ConvertWcsToScr(mep).ToSKPoint();
                    var pen = this.GetDrawPen(element);

                    if (pen == Constants.defaultPen)
                    {
                        using (var elePen = new SKPaint { Color = new SKColor(element.GetColorValue()), IsStroke = true })
                        {
                            canvas.DrawLine(ssp, sep, elePen);
                        }
                    }
                    else
                    {
                        canvas.DrawLine(ssp, sep, pen);
                    }
                }
            }
            foreach (var embedSlab in slab.UpSlabs)
            {
                for (int i = 0; i < embedSlab.Outline.Count - 1; i++)
                {
                    var sp = embedSlab.Outline[i];
                    var ep = embedSlab.Outline[i + 1];
                    var msp = matrix.MultiplyPoint(sp);
                    var mep = matrix.MultiplyPoint(ep);

                    var ssp = this.vportRt.ConvertWcsToScr(msp).ToSKPoint();
                    var sep = this.vportRt.ConvertWcsToScr(mep).ToSKPoint();
                    var pen = this.GetDrawPen(element);

                    if (pen == Constants.defaultPen)
                    {
                        using (var elePen = new SKPaint { Color = new SKColor(element.GetColorValue()), IsStroke = true, PathEffect = SKPathEffect.CreateDash(new float[] { 3, 3 }, 10) })
                        {
                            canvas.DrawLine(ssp, sep, elePen);
                        }
                    }
                    else
                    {
                        canvas.DrawLine(ssp, sep, pen);
                    }
                }
            }
            foreach (var embedSlab in slab.DownSlabs)
            {
                for (int i = 0; i < embedSlab.Outline.Count - 1; i++)
                {
                    var sp = embedSlab.Outline[i];
                    var ep = embedSlab.Outline[i + 1];
                    var msp = matrix.MultiplyPoint(sp);
                    var mep = matrix.MultiplyPoint(ep);

                    var ssp = this.vportRt.ConvertWcsToScr(msp).ToSKPoint();
                    var sep = this.vportRt.ConvertWcsToScr(mep).ToSKPoint();
                    var pen = this.GetDrawPen(element);

                    if (pen == Constants.defaultPen)
                    {
                        using (var elePen = new SKPaint { Color = new SKColor(element.GetColorValue()), IsStroke = true, PathEffect = SKPathEffect.CreateDash(new float[] { 3, 3 }, 10) })
                        {
                            canvas.DrawLine(ssp, sep, elePen);
                        }
                    }
                    else
                    {
                        canvas.DrawLine(ssp, sep, pen);
                    }
                }
            }
        }

        public override ControlGrip[] GetControlGrips(LcElement element)
        {
            var slab = element as QdSlab;
            var grips = new List<ControlGrip>();

            foreach (var point in slab.OutLine)
            {
                var gripCenter = new ControlGrip
                {
                    Element = slab,
                    Name = "Outline",
                    Position = point
                };
                grips.Add(gripCenter);
            }

            for (int i = 0; i < slab.Holes.Count; i++)
            {
                foreach (var point in slab.Holes[i])
                {
                    var gripCenter = new ControlGrip
                    {
                        Element = slab,
                        Name = "Innerline(" + i + ")",
                        Position = point
                    };
                    grips.Add(gripCenter);
                }
            }
            return grips.ToArray();
        }
    }
}